package com.example.java_chatroom.api;

import com.example.java_chatroom.component.OnlineUserManager;
import com.example.java_chatroom.model.*;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Component
public class WebSocketAPI extends TextWebSocketHandler {
    @Autowired
    private FriendReqMapper friendReqMapper;

    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private MessageSessionMapper messageSessionMapper;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private ObjectMapper objectMapper;


    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("[WebSocketAPI] 连接成功");
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        // 把这个键值对存起来
        onlineUserManager.online(user.getUserId(), session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("[WebSocketAPI] 收到消息: " + message.toString());

        // 针对请求进行解析 把 json 格式的字符串, 转换成 Java 中的对象
        MessageRequest req = objectMapper.readValue(message.getPayload(), MessageRequest.class);
        // 先获取到当前用户的信息, 后续要进行消息转发啥的
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            System.out.println("[WebSocketAPI -> handleTextMessage] user == null! 未登录用户, 无法进行消息转发");
            return;
        }
        switch (req.getType()) {
            case "message":
                // 进行消息转发
                transferMessage(user, req);
                break;
            case "friendRequest":
                // 进行好友申请转发
                transferFriendRequest(req);
                break;
            case "acceptFriendRequest":
                // 接受好友请求
                transferAcceptRequest(user, req);
                break;
            default:
                System.out.println("[WebSocketAPI -> handleTextMessage] req.type 有误! " + message.getPayload());
                break;
        }
    }

    private void transferAcceptRequest(User user, MessageRequest req) throws IOException {
        // 接受好友申请之后, 在后端创建一个消息会话, 并且发送一个消息给对方
        MessageSession messageSession = new MessageSession();
        messageSessionMapper.addMessageSession(messageSession);

        MessageSessionUserItem item1 = new MessageSessionUserItem();
        item1.setSessionId(messageSession.getSessionId());
        item1.setUserId(user.getUserId());
        messageSessionMapper.addMessageSessionUser(item1);

        MessageSessionUserItem item2 = new MessageSessionUserItem();
        item2.setSessionId(messageSession.getSessionId());
        item2.setUserId(req.getRequestId());
        messageSessionMapper.addMessageSessionUser(item2);


        // 发送一条消息, 其实只要把消息插入到数据库中就行, 前端在这个地方会刷新会话列表
        Message message = new Message();
        message.setFromId(user.getUserId());
        message.setSessionId(messageSession.getSessionId());
        message.setContent("我们已经添加为好友了, 现在可以开始聊天了");
        messageMapper.add(message);


        // req 中的 requestId 为要发送对象的 id
        WebSocketSession webSocketSession1 =
                onlineUserManager.getSession(req.getRequestId());

        WebSocketSession webSocketSession2 =
                onlineUserManager.getSession(user.getUserId());

        System.out.println("[transferAcceptRequest] 接受好友请求, 发起方 id 为: " + req.getRequestId());
        MessageResponse response = new MessageResponse();
        response.setType("acceptFriendRequest");
        if (webSocketSession1 == null) {
            // 如果该用户未在线, 不发送
            return;
        }
        if (webSocketSession2 == null) {
            // 如果该用户未在线, 不发送
            return;
        }
        String respJson = objectMapper.writeValueAsString(response);

        webSocketSession1.sendMessage(new TextMessage(respJson));
        webSocketSession2.sendMessage(new TextMessage(respJson));
    }

    private void transferFriendRequest(MessageRequest req) throws IOException {
        // req 中的 receiver 为要发送对象的名字, 首先需要根据名字查找 id
        int toSendId = friendReqMapper.getUserIdByName(req.getReceiver());
        WebSocketSession webSocketSession =
                onlineUserManager.getSession(toSendId);

        System.out.println("[transferFriendRequest] 发送好友请求, 对方 id 为: " + toSendId);
        MessageResponse response = new MessageResponse();
        response.setType("friendRequest");
        if (webSocketSession == null) {
            // 如果该用户未在线, 不发送
            return;
        }
        String respJson = objectMapper.writeValueAsString(response);

        webSocketSession.sendMessage(new TextMessage(respJson));
    }

    /**
     * 通过这个方法来完成消息实际转发的工作
     *
     * @param fromUser 表示要转发的消息是从谁那来的
     * @param req
     */
    private void transferMessage(User fromUser, MessageRequest req) throws IOException {
        // 1. 先构造一个待转发的响应对象
        MessageResponse resp = new MessageResponse();

        resp.setType("message");
        resp.setFromId(fromUser.getUserId());
        resp.setFromName(fromUser.getUsername());
        resp.setSessionId(req.getSessionId());
        resp.setContent(req.getContent());
        // 把这个 Java 对象转成 Json 字符串
        String respJson = objectMapper.writeValueAsString(resp);
        System.out.println("[WebSocketAPI -> transferMessage] respJson: " + respJson);

        // 2. 根据请求中的 sessionId, 获取到这个 messageSession 里都有哪些用户, 通过查询数据库获取
        List<Friend> friends =
                messageSessionMapper.getFriendsBySessionId(req.getSessionId(), fromUser.getUserId());
        // 上述数据库查询会把当前发消息的用户排除掉, 但是消息转发的时候需要把发送消息的人也发一个
        // 把当前用户也添加进 list 里面
        Friend myself = new Friend();
        myself.setFriendId(fromUser.getUserId());
        myself.setFriendName(fromUser.getUsername());
        friends.add(myself);

        // 3. 循环遍历上述列表, 给列表中的每个用户都发一个响应消息
        //    注意: 这里除了给查询到的好友发, 同时也要给自己发一份, 方便实现在自己的客户端上显示自己发送的消息
        //    注意: 一个会话中, 可能有多个用户(群聊), 虽然客户端没有支持群聊, 后端无论是 api 还是 数据库
        //          都是支持群聊的, 此处的转发逻辑也一样让它支持群聊
        for (Friend friend : friends) {
            //    知道了每个用户的 userId 查询刚才准备好的 onlineUserManager, 就知道了 对应点的 webSocketSession
            //    从而进行发送消息
            WebSocketSession webSocketSession =
                    onlineUserManager.getSession(friend.getFriendId());
            if (webSocketSession == null) {
                // 如果该用户未在线, 不发送
                continue;
            }
            webSocketSession.sendMessage(new TextMessage(respJson));
        }

        // 4. 转发的消息, 需要放到数据库里面, 后续用户下线, 再次上线之后, 还能从数据库中获取历史消息
        //    需要往 message 表中写入一条记录
        Message message = new Message();
        message.setFromId(fromUser.getUserId());
        message.setSessionId(req.getSessionId());
        message.setContent(req.getContent());
        // 自增主键和时间这样的属性, 都可以让 SQL 在数据库中生成
        messageMapper.add(message);
        messageSessionMapper.updateLastTimeFromMessageSession(req.getSessionId());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("[WebSocketAPI] 连接异常: " + exception.toString());

        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        onlineUserManager.offline(user.getUserId(), session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("[WebSocketAPI] 连接关闭: " + status.toString());

        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        onlineUserManager.offline(user.getUserId(), session);
    }
}
